package Simulator;

import java.util.Random;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Collections;

public class Simulator {
	private static final int DEFAULT_WIDTH = 50;
	private static final int DEFAULT_DEPTH = 50;
	// The probability that a fox will be created in any given grid position.
	private static final double FOX_CREATION_PROBABILITY = 0.04;
	// The probability that a rabbit will be created in any given grid position.
	private static final double RABBIT_CREATION_PROBABILITY = 0.10;
	// The probability that a bird will be created in any given grid position.
	private static final double BIRD_CREATION_PROBABILITY = 0.12;
	// The probability that a hunter will be created in any given grid position.
	private static final double HUNTER_CREATION_PROBABILITY = 0.01;

	// The list of actors in the field
	private List<ActorInterface> actors;
	// The list of actors just born
	private List<ActorInterface> newActors;
	// The current state of the field.
	private Field field;
	// A second field, used to build the next stage of the simulation.
	private Field updatedField;
	// The current step of the simulation.
	private int step;
	// A graphical view of the simulation.
	private static GenerationView view;
	// The simulation of grass.
	private Grass grassField;
	// The number of grass on the field.
	private double grassNumber;

	public Simulator() {
		this(DEFAULT_DEPTH, DEFAULT_WIDTH);
	}

	public Simulator(int depth, int width) {
		if (width <= 0 || depth <= 0) {
			System.out.println("The dimensions must be greater than zero.");
			System.out.println("Using default values.");
			depth = DEFAULT_DEPTH;
			width = DEFAULT_WIDTH;
		}
		actors = new ArrayList<ActorInterface>();
		newActors = new ArrayList<ActorInterface>();
		grassField = new Grass();
		grassNumber = depth * width;

		view = new GenerationView(depth, width);
		setSize(depth, width);

		reset();
	}

	public void makeView() {
		view.setAlwaysOnTop(true);
	}

	public static GenerationView getView() {
		return view;
	}

	public void setSize(int depth, int width) {
		field = new Field(depth, width);
		updatedField = new Field(depth, width);
		view.setFieldSize(depth, width);
		makeView();
	}

	public void simulate(int numSteps, int speed) {
		for (int step = 1; step <= numSteps && view.isViable(field); step++) {
			simulateOneStep(speed);
		}
	}

	public void simulateOneStep(int speed) {
		step++;
		newActors.clear();

		// let all animals act
		for (Iterator<ActorInterface> it = actors.iterator(); it.hasNext();) {
			ActorInterface actor = it.next();
			actor.act(field, updatedField, newActors);
			// Remove dead animals from the simulation.
			if (actor instanceof AbstactAnimal) {
				AbstactAnimal animal = (AbstactAnimal) actor;
				if (!animal.isAlive()) {
					it.remove();
				} else if (actor instanceof Rabbit) {
					Rabbit rabbit = (Rabbit) actor;
					double grassNew = rabbit.findFood(grassField.getGrass(),
							grassNumber);
					grassField.setGrass(grassNew);
				} else if (actor instanceof Bird) {
					Bird bird = (Bird) actor;
					double grassNew = bird.findFood(grassField.getGrass(),
							grassNumber);
					grassField.setGrass(grassNew);
				}
			}
		}
		// add new born animals to the list of animals
		actors.addAll(newActors);

		// Swap the field and updatedField at the end of the step.
		Field temp = field;
		field = updatedField;
		updatedField = temp;
		updatedField.clear();

		// Display the new field on screen.
		view.showStatus(step, field);

		// Let the grass grow
		grassField.grow(grassNumber);

		// Have a break between the steps
		try {
			Thread.sleep(speed);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public void reset() {
		step = 0;
		actors.clear();
		newActors.clear();
		field.clear();
		updatedField.clear();
		grassField.setGrass(grassNumber);
		populate(field);
		view.showStatus(step, field);
	}

	private void populate(Field field) {
		Random rand = new Random();
		field.clear();
		for (int row = 0; row < field.getDepth(); row++) {
			for (int col = 0; col < field.getWidth(); col++) {
				if (rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
					Fox fox = new Fox(true);
					fox.setLocation(row, col);
					actors.add(fox);
					field.place(fox);
				} else if (rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) {
					Rabbit rabbit = new Rabbit(true);
					rabbit.setLocation(row, col);
					actors.add(rabbit);
					field.place(rabbit);
				} else if (rand.nextDouble() <= BIRD_CREATION_PROBABILITY) {
					Bird bird = new Bird(true);
					bird.setLocation(row, col);
					actors.add(bird);
					field.place(bird);
				} else if (rand.nextDouble() <= HUNTER_CREATION_PROBABILITY) {
					Hunter hunter = new Hunter();
					hunter.setLocation(row, col);
					actors.add(hunter);
					field.place(hunter);
				}
			}
		}
		Collections.shuffle(actors);
	}
}
